/*
 * Copyright (c) 2022 iocui.com All rights reserved.
 * =============================================================================
 * 实现自动加载 components 子目录下的 Vite 插件模块，支持开发时插件扩展热更新
 * -----------------------------------------------------------------------------
 * 注意：子目录内的插件仅服务于开发环境，非生产环境产物, 和vue插件自动化导入有本质区别
 * =============================================================================
 * Author:      泡泡鱼<emcao@foxmail.com>
 * Created:     2022-02-11
 * Modified:    2025-05-23
 * License:     MIT License
 */

//@ts-ignore
import semver from 'semver';

import {
  type Plugin,
  type PluginOption,
  normalizePath,
  version as viteVersion
} from 'vite';

import fs from 'fs';
import path from 'path';

//===================================================================
// 预构建项目所用到的vite插件配置文件, 仅用于 vite < 5.0.0 版本开发环境
//===================================================================
const bundleConfigFile = async (root: string, fileName: string, isESM = false) => {
  const result = await require('esbuild').build({
    absWorkingDir: root,
    entryPoints: [fileName],
    outfile: 'out.js',
    write: false,
    platform: 'node',
    bundle: true,
    format: isESM ? 'esm' : 'cjs',
    sourcemap: false, //'inline',
    metafile: true,
    plugins: [
      {
        name: 'externalize-deps',
        setup(build: any) {
          // 加载文件时触发
          build.onResolve({ filter: /.*/ }, (args: any) => {
            const id = args.path;
            if (id[0] !== '.' && !path.isAbsolute(id)) {
              return {
                external: true
              };
            }
            return {
              external: false
            };
          });
        }
      },
      {
        name: 'replace-import-meta',
        setup(build: any) {
          build.onLoad({ filter: /\.[jt]s$/ }, async (args: any) => {
            const contents = fs.readFileSync(args.path, 'utf8');
            return {
              loader: args.path.endsWith('.ts') ? 'ts' : 'js',
              contents: contents
                .replace(/\bimport\.meta\.url\b/g, JSON.stringify(`file://${args.path}`))
                .replace(/\b__dirname\b/g, JSON.stringify(path.dirname(args.path)))
                .replace(/\b__filename\b/g, JSON.stringify(args.path))
            };
          });
        }
      }
    ]
  });
  const { text } = result.outputFiles[0];
  return {
    code: text,
    dependencies: result.metafile ? Object.keys(result.metafile.inputs) : []
  };
};

//===================================================================
// 扩展插件作用：修改指定目录文件自动重启vite服务 By PGcao
//===================================================================
const autoRestartVite = (root: string): Plugin => {
  let timer: NodeJS.Timeout;
  const changeConfigFile = (path: string) => {
    const time = new Date();
    try {
      fs.utimesSync(path, time, time);
    } catch (err) {
      fs.closeSync(fs.openSync(path, 'w'));
    }
  };
  return {
    name: 'vite-plugin-auto-restart',
    apply: 'serve',
    configureServer({ watcher }) {
      const checkReload = async (eventName: string, pathname: string) => {
        pathname = normalizePath(pathname);
        if (pathname.includes('.d.ts') || pathname.includes('vite.config.ts')) {
          return;
        }

        let isReload = false;
        if (pathname.includes(`src/assets/globals.scss`)) {
          if (['add', 'change', 'unlink'].includes(eventName)) {
            isReload = true;
          }
        } else if (pathname.includes(`src/layouts/`)) {
          if (['add', 'change', 'unlink'].includes(eventName)) {
            isReload = true;
          }
        } else if (pathname.includes(`src/hooks/`)) {
          if (['add', 'change', 'unlink'].includes(eventName)) {
            isReload = true;
          }
        }

        // console.log(isReload, root, pathname, eventName);

        if (isReload) {
          if (timer) clearTimeout(timer);
          const configPath = path.resolve(root, 'vite.config.ts');
          timer = setTimeout(() => {
            if (fs.existsSync(configPath)) {
              changeConfigFile(configPath);
            }
          }, 100);
        }
      };
      watcher.on('all', checkReload);
    }
  };
};

export default async (options: viteUserOptions): Promise<PluginOption[]> => {
  const version = process.version;
  console.log(`nodeJS run ${version}\r\n`);

  console.log('🚀 Starting to load Vite plugins...');

  const pluginsDir = normalizePath(path.resolve(__dirname, 'components'));
  const entries = fs.readdirSync(pluginsDir)
    .filter((name) => !name.startsWith('@')) // 忽略含有@符号的目录
    .filter((name) => fs.existsSync(path.join(pluginsDir, name, 'index.ts'))); // 过滤不存在 index.ts 的目录

  // 生成多层文件夹
  const mkdirsSync = (dirname: string) => {
    if (fs.existsSync(dirname)) {
      return true;
    } else {
      if (mkdirsSync(path.dirname(dirname))) {
        fs.mkdirSync(dirname);
        return true;
      }
    }
    return false;
  };

  const root = options.path.root;
  const modulesPath = options.path.modules;
  const pluginModules = await Promise.all(
    entries.map(async (dirname) => {
      let writeCacheFile = false, isuseRequire = false;
      const cacheFile = `${modulesPath}/.cache/vite-plugins/${dirname}.cjs`;

      if (semver.lt(viteVersion, '5.0.0')) {
        isuseRequire = true;
        const tsFile = path.join(pluginsDir, dirname, 'index.ts');
        if (!mkdirsSync(path.dirname(cacheFile))) {
          throw new Error(`vite plugins cache path not found`);
        }

        if (fs.existsSync(cacheFile)) {
          const statsObj = fs.statSync(tsFile);
          const cacheObj = fs.statSync(cacheFile);
          if (statsObj.mtimeMs > cacheObj.mtimeMs) {
            delete require.cache[require.resolve(cacheFile)];
            writeCacheFile = true;
          }
        } else {
          writeCacheFile = true;
        }

        if (writeCacheFile) {
          const bundled = await bundleConfigFile(root, tsFile);
          fs.writeFileSync(cacheFile, bundled.code);
          require.cache[require.resolve(cacheFile)];
        }
      }

      try {
        const mod = isuseRequire ? require(cacheFile) : await import(/* @vite-ignore */ `./components/${dirname}/index.ts`);
        const result = typeof mod.default === 'function' ? await mod.default(options) : mod.default || mod;
        if (Array.isArray(result) || typeof result === 'object') {
          return result;
        } else {
          console.log(`⚠️  Disable the plugin: ${dirname}`);
        }
      } catch (err) {
        const filePath = `${pluginsDir}/${dirname}/index.ts`;
        console.log(`❌ Failed to load plugin: ${dirname}\r\n`, 'File: ' + filePath, err);
      }
    })
  );

  // 修改指定目录内文件，自动重启vite服务
  pluginModules.push(autoRestartVite(options.path.root));

  console.log('✅ Vite plugins loaded successfully.');

  // 扁平化并过滤无效项
  return pluginModules.flat().filter(plugin => !!plugin);
};
